package fr.cephb.berkeley;

import com.sleepycat.db.DatabaseEntry;

import fr.cephb.lang.Endian;
/**
 * An utility class reading simple type (int8,int16...) from an array of byte 
 * or from a  com.sleepycat.db.DatabaseEntry
 * <b>Warning</b>: this class is <b>INDIAN sensitive</b> see {@link fr.cephb.lang.Endian fr.cephb.lang.Endian}
 *
 */
public class ByteReader
extends DBUtils
{
private int length=-1;
private byte array[];
private int pos=0;
/** Constructor with a  com.sleepycat.db.DatabaseEntry */
public ByteReader(DatabaseEntry entry)
	{
	this(entry.getData(),entry.getSize());
	}

/** Constructor with a  com.sleepycat.db.DatabaseEntry and reading at a given offset of
 * the byte of array. Motivation: reading a given item in a concatened array of byte 
 * @param entry the entry
 * @param pos the index in the array of byte
 * */
public ByteReader(DatabaseEntry entry,int pos)
	{
	this(entry.getData(),entry.getSize(),pos);
	}

/**
 * Constructor with an array of bytes
 * @param array the array of byte
 * @param length the length of the array
 */
public ByteReader(byte array[],int length)
	{
	this(array,length,0);
	}

/**
 * Constructor with an array of bytes
 * @param array the array of byte
 * @param length the length of the array
 * @param pos the index of the array
 */
public ByteReader(byte array[],int length,int pos)
	{
	this.array=array;
	this.pos=pos;
	this.length=length;
	}

/**
 * Constructor with an array of bytes
 * @param array the array of bytes

 */
public ByteReader(byte array[])
	{
	this(array,array.length);
	}

/** return the array of byte */
public byte[] getBytes()
	{
	return array;
	}

/** return the current index */
public int getIndex()
	{
	return this.pos;
	}

/** 
 * read a int16
 * @see  fr.cephb.lang.Endian
 */
public short readInt16()
	{
	if(pos+1>= this.length) throw new IndexOutOfBoundsException();
	short number;
	if(Endian.getDefault()==Endian.CNG)
		{
		number= (short)(
				 	 (array[pos+1]<< 8)
		            | array[pos+0]);
		}
	else
		{
		number= (short)(
			 	 (array[pos+0]<< 8)
	            | array[pos+1]);
		}
	
	pos+=2;
	return number;
	}

/** 
 * read an unsigned int32
 * @see  fr.cephb.lang.Endian
 */
public long readUInt32()
	{
	if(pos+3>= this.length) throw new IndexOutOfBoundsException("pos="+pos+" length="+this.length);
	if(Endian.getDefault()==Endian.CNG)
		{
		long l = ((long)uget()) ;
		     l |= ((long)uget()) << 8;
		     l |= ((long)uget()) << 16;
		     l |= (long)uget()<< 24 ; 
		    
	    return l;
		}
	else
		{

		long l = ((long)uget()<<24) ;
	     l |= ((long)uget()) << 16;
	     l |= ((long)uget()) << 8;
	     l |= (long)uget() ;
	     return l;
		}
	}

/** 
 * read an unsigned int16
 * @see  fr.cephb.lang.Endian
 */
public int readUInt16()
	{
	if(true) throw new UnsupportedOperationException("TODO: not tested for indianess");
	
	int result = uget()<< 8;
	result |= uget();
	return result;
	}

private short uget() //get unsigned
	{
	if(pos+1> this.length) throw new IndexOutOfBoundsException("Error pos="+pos+"+1>="+this.length);
	byte b=array[pos];
	++pos;
	return  (short) (0xFF & b);
	}

/** 
 * read an unsigned int8
 * @see  fr.cephb.lang.Endian
 */
public short readUInt8()
	{
	if(true) throw new UnsupportedOperationException("TODO: not tested for indianess");
	return   uget();
	}

/** 
 * read an int8
 */
public byte readInt8()
	{
	if(pos+1> this.length) throw new IndexOutOfBoundsException("Error pos="+pos+"+1>="+this.length);
	byte b=array[pos];
	++pos;
	return   b;
	}


/** 
 * read an int32
 * @see  fr.cephb.lang.Endian
 */
public int readInt32()
	{
	if(pos+3>= this.length) throw new IndexOutOfBoundsException();
	int number;
	
	if(Endian.getDefault()==Endian.CNG)
		{
		number =  (int)((array[pos+3] & 0xff) << 24)
		 			| (int)((array[pos+2] & 0xff) << 16)
		 			| (int)((array[pos+1] & 0xff) << 8)
		 			|  (int)(array[pos+0] & 0xff);
		}
	else
		{
		number =  (int)((array[pos+0] & 0xff) << 24)
			| (int)((array[pos+1] & 0xff) << 16)
			| (int)((array[pos+2] & 0xff) << 8)
			|  (int)(array[pos+3] & 0xff);
		}
	pos+=4;
	return number;
	}

/** 
 * read an int64
 * @see  fr.cephb.lang.Endian
 */
public long readInt64()
	{
	
	if(Endian.getDefault()==Endian.CNG)
		{
		long a= readInt32();
		long b= readInt32();
		long	number = a | (b << 32);
		return number;
		}
	else
		{
		throw new UnsupportedOperationException("check indian");
		}
	}

/** 
 * read an float 64 bytes
 */
public double readFloat64()
	{
	return Double.longBitsToDouble(readInt64());
	}

/** 
 * read an float 32 bytes
 */
public float readFloat32()
	{
	return Float.intBitsToFloat(readInt32());
	}



/** 
 * read a C string ending with the '\0' character
 */
public String readCString()
	{
	int n=pos;
	if(n>=this.length)
		{
		throw new java.lang.ArrayIndexOutOfBoundsException(
				"Cannot start reading array n="+ n + " and array.length = "+this.array.length
				);
		}
	while(array[n]!=0)
		{
		++n;
		if(n==this.length)
			{
			throw new java.lang.ArrayIndexOutOfBoundsException(
				"End Of String not found: just read("+ new String(array,pos,n-pos)+")"
				);
			}
		}
	String s= new String(array,pos,n-pos);
	pos=n+1;
	return s;
	}

}
